package tcp_server

import (
	"context"
	"math"
	"net"
)

const abortIndex int8 = math.MaxInt8 / 2 //最多 63 个中间件

// 路由树
type TcpSliceRouter struct {
	groups []*TcpSliceGroup
}

var _ TCPHandler = (*TcpSliceRouter)(nil)

func NewTcpSliceRouter() *TcpSliceRouter {
	return &TcpSliceRouter{}
}

// 创建 Group，只有唯一的 / group
func (g *TcpSliceRouter) Group(path string) *TcpSliceGroup {
	if path != "/" {
		panic("only accept path=/")
	}
	if len(g.groups) > 0 {
		return g.groups[0]
	}
	group := &TcpSliceGroup{
		path: path,
	}
	g.groups = append(g.groups, group)

	return group
}

func (r *TcpSliceRouter) ServeTCP(ctx context.Context, conn net.Conn) {
	// 1、构造自定义的 context
	c := newTcpSliceRouterContext(conn, ctx)

	// 2、查找路由
	newTcpSliceGroup := &TcpSliceGroup{}
	// 只会使用第一个分组，浅拷贝数组指针
	*newTcpSliceGroup = *(r.groups[0])
	c.TcpSliceGroup = newTcpSliceGroup

	// 3、启动责任链
	c.Reset()
	c.Next()
}

// 路由方法
type TcpHandlerFunc func(*TcpSliceRouterContext)

// 路由节点
type TcpSliceGroup struct {
	// *TcpSliceRouter
	path     string
	handlers []TcpHandlerFunc
}

// 给路由节点挂载中间件
func (g *TcpSliceGroup) Use(middlewares ...TcpHandlerFunc) *TcpSliceGroup {
	g.handlers = append(g.handlers, middlewares...)
	return g
}

// router 上下文
type TcpSliceRouterContext struct {
	Ctx  context.Context
	Conn net.Conn

	// 匹配上的路由节点
	*TcpSliceGroup
	index int8
}

func newTcpSliceRouterContext(conn net.Conn, ctx context.Context) *TcpSliceRouterContext {
	c := &TcpSliceRouterContext{Conn: conn, Ctx: ctx}
	return c
}

func (c *TcpSliceRouterContext) Get(key interface{}) interface{} {
	return c.Ctx.Value(key)
}

func (c *TcpSliceRouterContext) Set(key, val interface{}) {
	c.Ctx = context.WithValue(c.Ctx, key, val)
}

// 责任链调度
func (c *TcpSliceRouterContext) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}

// 终止责任链
func (c *TcpSliceRouterContext) Abort() {
	c.index = abortIndex
}

// 是否已终止责任链
func (c *TcpSliceRouterContext) IsAborted() bool {
	return c.index >= abortIndex
}

// 重置责任链
func (c *TcpSliceRouterContext) Reset() {
	c.index = -1
}
